package com.gobi;


import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphO;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.property.SentinelProperty;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.fastjson.JSON;

import java.util.ArrayList;
import java.util.List;

/**
 * 使用 Sentinel 需要我们提供限流规则，在规则的基础上，将临界区代码使用限流作用域结构包裹起来。
 * Sphu#entry方法就会抛出限流异常 BlockException
 * SphO#entry方法返回bool 形式
 */
public class SentinelTest {

    /**
     * 在上面的例子中限定了 tutorial 资源的单机 QPS 不得超出 1，但是实际上它的运行 QPS 是 2，这多出来的执行逻辑就会被限制，
     * 对应的 Sphu.entry() 方法就会抛出限流异常 BlockException。
     */
    public static void main(String[] args) {
//        testSphU();
//        testSphO();
        testDegradeRule();
    }

    private static void testSphU() {
        // 配置规则
        FlowRule rule = new FlowRule();
        rule.setResource("tutorial");
        // QPS 不得超出 1
        rule.setCount(1);
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setLimitApp("default");

        List<FlowRule> rules = new ArrayList<>();
        rules.add(rule);
        // 加载规则
        FlowRuleManager.loadRules(rules);

        // 下面开始运行被限流作用域保护的代码
        while (true) {
            Entry entry = null;
            try {
                entry = SphU.entry("tutorial");
                System.out.println("hello world");
            } catch (BlockException e) {
                System.err.println("blocked");
            } finally {
                if (entry != null) {
                    entry.exit();
                }
            }
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
        }
    }


    private static void testSphO() {
        // 配置规则
        FlowRule rule = new FlowRule();
        rule.setResource("tutorial");
        // QPS 不得超出 1
        rule.setCount(1);
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setLimitApp("default");


        List<FlowRule> rules = new ArrayList<>();
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
        // 运行被限流作用域保护的代码
        while (true) {
            if (SphO.entry("tutorial")) {
                try {
                    System.out.println("hello world");
                } finally {
                    SphO.exit();
                }
            } else {
                System.out.println("blocked");
            }
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
        }
    }

    /**
     * 熔断降级
     */
    public static void testDegradeRule() {
        //degrade 英 [dɪˈɡreɪd] 使)退化，降解
        // 定义降级规则
        List<DegradeRule> rules = new ArrayList<>();
        DegradeRule rule = new DegradeRule();
        rule.setResource("tutorial");
        // 5s内异常不得超出10
        rule.setCount(10);
        rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
        rule.setLimitApp("default");
        rules.add(rule);
        DegradeRuleManager.loadRules(rules);

        Entry entry = null;
        try {
            entry = SphU.entry("tutorial");
            // 业务代码在这里
        } catch (Throwable t) {
            // 记录异常
            if (!BlockException.isBlockException(t)) {
                Tracer.trace(t);
            }
        } finally {
            if (entry != null) {
                entry.exit();
            }
        }
    }

    /**
     * sentinel监控使用redis持久化规则
     */
    public void test0() {
        // redis 地址
        RedisConnectionConfig redisConf = new RedisConnectionConfig("localhost", 6379, 1000);
        // 反序列化算法
        Converter<String, List<FlowRule>> converter = r -> JSON.parseArray(r, FlowRule.class);
        // 定义规则源，包含全量和增量部分
        // 全量是一个字符串key，增量是 pubsub channel key
        ReadableDataSource<String, List<FlowRule>> redisDataSource = new RedisDataSource<List<FlowRule>>(redisConf, "app_key", "app_pubsub_key", converter);

        FlowRuleManager.register2Property(redisDataSource.getProperty());
    }

    private class RedisConnectionConfig {
        public RedisConnectionConfig(String localhost, int i, int i1) {
        }
    }

    private class RedisDataSource<T> implements ReadableDataSource<String, List<FlowRule>> {
        public RedisDataSource(RedisConnectionConfig redisConf, String app_key, String app_pubsub_key, Converter<String, T> converter) {

        }

        @Override
        public List<FlowRule> loadConfig() throws Exception {
            return null;
        }

        @Override
        public String readSource() throws Exception {
            return null;
        }

        @Override
        public SentinelProperty<List<FlowRule>> getProperty() {
            return null;
        }

        @Override
        public void close() throws Exception {

        }
    }
}
